package com.ld.zxw.index;

import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.util.List;

import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.TopFieldCollector;
import org.apache.lucene.search.highlight.Fragmenter;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.InvalidTokenOffsetsException;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleFragmenter;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.ld.zxw.config.LucenePlusConfig;
import com.ld.zxw.page.Page;
import com.ld.zxw.util.CommonUtil;

public class IndexDao {

	private LucenePlusConfig config;
	private Highlighter highlighter;
	private final static IndexDao dao = new IndexDao();

	private IndexDao() {
	}

	public static synchronized IndexDao build(LucenePlusConfig config){
		dao.highlighter = null;
		dao.config = config;
		return dao;
	}
	public void saveIndex(List<Document> list) throws IOException{
		IndexWriter writer = this.config.getIndexWriter();
		writer.addDocuments(list);
		//刷新查询接口
		CommonUtil.refresh(this.config, writer);
	}
	public void saveIndex(Document doc) throws IOException{
		IndexWriter writer = this.config.getIndexWriter();
		writer.addDocument(doc);
		//刷新查询接口
		CommonUtil.refresh(this.config, writer);
	}

	public void delAll() throws IOException{
		IndexWriter writer = this.config.getIndexWriter();
		writer.deleteAll();
		CommonUtil.refresh(this.config, writer);
	}

	public void deletekey(Term term) throws IOException{
		IndexWriter writer = this.config.getIndexWriter();
		writer.deleteDocuments(term);
		CommonUtil.refresh(this.config, writer);
	}
	public void deletekey(Query query) throws IOException{
		IndexWriter writer = this.config.getIndexWriter();
		writer.deleteDocuments(query);
		CommonUtil.refresh(this.config, writer);
	}

	public void updateIndex(List<Document> list,Term term) throws IOException{
		IndexWriter writer = this.config.getIndexWriter();
		writer.updateDocuments(term, list);
		CommonUtil.refresh(this.config, writer);
	}
	public void updateIndex(Document doc,Term term) throws IOException{
		IndexWriter writer = this.config.getIndexWriter();
		writer.updateDocument(term, doc);
		CommonUtil.refresh(this.config, writer);
	}

	public <T> List<T> findList(Query query,Class<T> obj,int num,Sort sort) throws IOException, InvalidTokenOffsetsException {
		IndexSearcher searcher = this.config.getIndexSearcher();
		TopDocs rs;
		if(num == 0){
			num = Integer.MAX_VALUE;
		}
		if(sort != null) {
			rs = searcher.search(query,num,sort);
		}else {
			rs = searcher.search(query,num);
		}
		QueryScorer scorer = new QueryScorer(query);  
		return dataHandle(searcher, rs.scoreDocs, scorer,obj);
	}

	public <T> Page<T> findList(Query query, int pageNumber,int pageSize, Class<T> obj,Sort sort) throws IOException, InvalidTokenOffsetsException {
		if(pageNumber == 0) {
			pageNumber = 1;
		}
		if(pageSize == 0){
			pageSize = 10;
		}
		if(sort == null){
			sort = new Sort();
		}
		int pageNum = (pageNumber-1) * pageSize;
		IndexSearcher searcher = this.config.getIndexSearcher(); 
		TopFieldCollector create = TopFieldCollector.create(sort, pageNum+pageSize, false, false, false);
		searcher.search(query,create);
		int totalHits = create.getTotalHits();
		ScoreDoc[] scoreDocs = create.topDocs(pageNum,pageSize).scoreDocs;
		QueryScorer scorer = new QueryScorer(query);
		List<T> dataHandle = dataHandle(searcher, scoreDocs, scorer, obj);
		int totalPage = (int) (totalHits / pageSize);
		if (totalHits % pageSize != 0) {
			totalPage++;
		}
		return new Page<T>(dataHandle, pageNumber, pageSize, totalPage, totalHits);
	}

	public <T> List<T> dataHandle(IndexSearcher searcher,ScoreDoc[] scoreDocs,QueryScorer scorer,Class<T> obj) throws IOException, InvalidTokenOffsetsException{
		int length = scoreDocs.length;
		List<T> list = Lists.newArrayList();
		if(this.config.isHighlight()) {
			this.highlighter = getHighlighter(scorer);
		}
		for (int i = 0; i < length; i++) {
			JSONObject object = new JSONObject();
			Document doc = searcher.doc(scoreDocs[i].doc);
			Field[] fields = obj.getDeclaredFields();
			for (int j = 0; j < fields.length; j++) {
				String name = fields[j].getName();
				String value = doc.get(name);
				setHighlig(object, name, value);
			}
			list.add(object.toJavaObject(obj));
		}
		return list;
	}

	public void setHighlig(JSONObject object,String name,String value) throws IOException, InvalidTokenOffsetsException{
		if((value == null || value.trim() == "") & !this.config.isHighlight()){//为空或者不需要高亮直接入对象
			object.put(name, value);
		}else if(this.config.isHighlight() & this.config.getHighlightFields() != null & this.config.getHighlightFields().contains(name)){//要高亮 并且设置高亮字段
			TokenStream tokenStream = this.config.getAnalyzer().tokenStream(name, new StringReader(value));
			String bestFragment = this.highlighter.getBestFragment(tokenStream, value);
			object.put(name, bestFragment == null ? value :bestFragment);
		}else{
			object.put(name, value);
		}
	}


	public Highlighter getHighlighter(QueryScorer scorer){
		Fragmenter fragmenter = new SimpleFragmenter();
		String[] highlight_conf = this.config.getHighlightConf();
		SimpleHTMLFormatter formatter = new SimpleHTMLFormatter(highlight_conf[0],highlight_conf[1]);
		Highlighter highlighter = new Highlighter(formatter,scorer);
		highlighter.setTextFragmenter(fragmenter);
		return highlighter;
	}

}
